package com.agimatec.selenium.base;

import com.agimatec.locators.AbstractLocatorBuilder;
import com.agimatec.locators.Locator;
import com.agimatec.selenium.base.exception.ElementNotFoundException;
import com.thoughtworks.selenium.DefaultSelenium;
import org.testng.Reporter;

/**
 * This class contains helpers for handling Ajax applications
 * <p/>
 * - post loading of scripts
 * - asynchronous change of DOM
 */
public class AjaxSelenium extends DefaultSelenium {
    private String serverHost;
    private int serverPort;
    private String browserStartCommand;
    private String browserURL;
    public static final int TIMEOUT = 15;

    public AjaxSelenium(String serverHost, int serverPort,
                        String browserStartCommand, String browserURL) {
        super(serverHost, serverPort, browserStartCommand, browserURL);
        this.serverHost = serverHost;
        this.serverPort = serverPort;
        this.browserStartCommand = browserStartCommand;
        this.browserURL = browserURL;
    }

    /**
     * Helper to click on a link not already present (asynchronous loaded)
     *
     * Please note that using this method should be limited to locators for which no
     * {@link com.agimatec.locators.Locator} implementation is availiable.
     *
     * @param locator an element locator
     * @throws Exception
     */
    public void safeClick(String locator) {
        this.waitForElement(locator);
        // to ensure that all javascript is loaded
        try {
            Thread.sleep(1500);
        } catch (InterruptedException e) {
            Reporter.log(e.getMessage());
            throw new RuntimeException(e);
        }
        this.click(locator);
    }

    /**
     * Helper to click on a link not already present (asynchronous loaded)
     *
     * @param locator an element locator
     * @throws Exception
     */
    public void safeClick(Locator locator) {
        safeClick(locator.toString());
    }

    /**
     * Helper to open a complex page, where javascript is loaded after the page load has finished
     *
     * @param pageUrl the URL to open; may be relative or absolute
     */
    public void safeOpen(String pageUrl) {
        this.open(pageUrl);

        // normal page load
        this.waitForPageToLoad("30000");

        this.waitForElement("//div[@id='javascript-loaded']");
    }

    /**
     * @param locator
     * @return
     * @deprecated Use the XPath version instead.
     */
    private String safeGetText(String locator) {
        this.waitForElement(locator);
        return this.getText(locator);
    }

    /**
     * Gets the text contained in the element the given locator points to. If the element is not inside the DOM,
     * this method waits for it to appear, failign after a timeout.
     *
     * @param locator XPath pointer to an element
     * @return Text contained inside the element
     */
    public String safeGetText(Locator locator) {
        this.waitForElement(locator);
        return this.getText(locator.toString());
    }


    /**
     * @param locator
     * @return
     * @throws ElementNotFoundException
     * @throws InterruptedException
     * @deprecated
     */
    public String safeGetValue(String locator) throws ElementNotFoundException, InterruptedException {
        this.waitForElement(locator);
        return this.getValue(locator);
    }

    /**
     * @param locator
     * @return
     * @throws ElementNotFoundException
     * @throws InterruptedException
     */
    public String safeGetValue(Locator locator) throws ElementNotFoundException, InterruptedException {
        return safeGetValue(locator.toString());
    }

    /**
     * @param locator
     * @param text
     * @throws ElementNotFoundException
     * @throws InterruptedException
     * @deprecated
     */
    public void safeType(String locator, String text) throws ElementNotFoundException, InterruptedException {
        this.waitForElement(locator);
        this.type(locator, text != null ? text : "");
    }

    /**
     * @param locator
     * @param text
     * @throws ElementNotFoundException
     * @throws InterruptedException
     */
    public void safeType(Locator locator, String text) throws ElementNotFoundException, InterruptedException {
        safeType(locator.toString(), text);
    }

    /**
     * Helper which waits for an element not already present
     *
     * @param locator an element locator
     * @deprecated Use the XPath version instead!
     */
    public void waitForElement(String locator) {
        for (int second = 0; ; second++) {
            if (second >= TIMEOUT) throw new ElementNotFoundException("Element not present: " + locator);
            try {
                if (this.isElementPresent(locator)) break;
            } catch (Exception e) {
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Reporter.log(e.getMessage());
                throw new RuntimeException(e);
            }
        }
    }


    /**
     * Waits for an element that might not be currently inside the DOM.
     *
     * @param locator XPath to the element to wait for
     */
    public void waitForElement(Locator locator) {
        for (int second = 0; ; second++) {
            if (second >= TIMEOUT) throw new ElementNotFoundException("Element not present: " + locator);
            try {
                if (this.isElementPresent(locator.toString())) break;
            } catch (Exception e) {
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Reporter.log(e.getMessage());
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * Helper which waits for an text element
     *
     * @param locator an element locator
     * @param text    the text to be waited for
     * @throws InterruptedException
     * @throws ElementNotFoundException
     */
    private void waitForText(String locator, String text) throws InterruptedException, ElementNotFoundException {
        for (int second = 0; ; second++) {
            if (second >= TIMEOUT)
                throw new ElementNotFoundException("Text '" + text + "' in element '" + locator + "' not present.");
            try {
                if (text.trim().equals(this.getText(locator).trim())) break;
            } catch (Exception e) {
            }
            Thread.sleep(1000);
        }
    }

    /**
     * Helper which waits for an text element to appear
     * @param locator An element locator
     * @param text The text to be waited for
     * @throws InterruptedException
     */
    public void waitForText(Locator locator, String text) throws InterruptedException {
        waitForText(locator.toString(),text);
    }

    /**
     * Helper which waits for an text element
     *
     * @param locator an element locator
     * @param text    the text to be waited for
     */
    public void waitForTextStart(Locator locator, String text) {
        for (int second = 0; ; second++) {
            if (second >= TIMEOUT)
                throw new ElementNotFoundException("Text '" + text + "' in element '" + locator + "' not present.");
            try {
                if (this.getText(locator.toString()).startsWith(text)) break;
            } catch (Exception e) {
                Reporter.log(e.getMessage());
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }


    /**
     * Helper which waits for a value of an input element
     *
     * @param locator an element locator
     * @param text    the text to be waited for
     * @throws ElementNotFoundException
     * @throws InterruptedException
     */
    public void waitForValue(Locator locator, String text) throws ElementNotFoundException, InterruptedException {
        for (int second = 0; ; second++) {
            if (second >= TIMEOUT)
                throw new ElementNotFoundException("Text '" + text + "' in element '" + locator + "' not present.");
            try {
                if (text.trim().equals(this.getValue(locator.toString()).trim())) break;
            } catch (Exception e) {
            }
            Thread.sleep(1000);
        }
    }

    public int getRowWithValue(String tableLocator, int column, String value) throws ElementNotFoundException, InterruptedException {
        int rowCount = this.getXpathCount(tableLocator + "//tr").intValue();

        for (int i = 1; i <= rowCount; i++) {
            if (this.safeGetText(tableLocator + "//tr[" + i + "]//td[" + column + "]").equals(value)) {
                return i;
            }
        }

        return -1;
    }

    public int getRowWithValue(AbstractLocatorBuilder tableLocator, int column, String value) throws ElementNotFoundException, InterruptedException {
        return getRowWithValue(tableLocator.toString(), column, value);
    }


    public String getServerHost() {
        return serverHost;
    }

    public void setServerHost(String serverHost) {
        this.serverHost = serverHost;
    }

    public int getServerPort() {
        return serverPort;
    }

    public void setServerPort(int serverPort) {
        this.serverPort = serverPort;
    }

    public String getBrowserStartCommand() {
        return browserStartCommand;
    }

    public void setBrowserStartCommand(String browserStartCommand) {
        this.browserStartCommand = browserStartCommand;
    }

    public String getBrowserURL() {
        return browserURL;
    }

    public void setBrowserURL(String browserURL) {
        this.browserURL = browserURL;
    }
}
